---
title: "Swift Type System and Optionals"
description: "Master Swift's type system and optional types, understanding type safety, nil handling, and type inference from a JavaScript perspective."
---

## 1. Introduction

### Why Type Systems Matter

Swift's type system is one of its most powerful features, providing compile-time safety that prevents many common programming errors. As a JavaScript developer, you're used to dynamic typing, but Swift's static type system will help you write more reliable and maintainable code.

**Key Learning Objectives:**
- Understand Swift's static type system vs JavaScript's dynamic typing
- Master optional types and nil safety
- Learn type inference and type annotations
- Explore type conversion and type checking
- Develop type-safe programming practices

## 2. Type System Fundamentals

### 2.1 Static vs Dynamic Typing

Swift uses static typing, which means types are checked at compile time, unlike JavaScript's dynamic typing.

<UniversalEditor title="Static vs Dynamic Typing" compare={true}>
```javascript !! js
// JavaScript - Dynamic typing
let value = "Hello";         // String
console.log(typeof value);   // "string"

value = 42;                  // Now it's a number
console.log(typeof value);   // "number"

value = true;                // Now it's a boolean
console.log(typeof value);   // "boolean"

value = [1, 2, 3];          // Now it's an array
console.log(typeof value);   // "object"

// Runtime type checking
function processValue(val) {
    if (typeof val === "string") {
        return val.toUpperCase();
    } else if (typeof val === "number") {
        return val * 2;
    } else {
        return "Unknown type";
    }
}

console.log(processValue("hello")); // "HELLO"
console.log(processValue(5));       // 10
console.log(processValue(true));    // "Unknown type"
```

```swift !! swift
// Swift - Static typing
var value: String = "Hello"         // Explicit String type
print(type(of: value))              // String

// These would cause compile errors:
// value = 42                        // Error: Cannot assign value of type 'Int' to type 'String'
// value = true                      // Error: Cannot assign value of type 'Bool' to type 'String'
// value = [1, 2, 3]                // Error: Cannot assign value of type '[Int]' to type 'String'

// Compile-time type checking
func processValue(_ val: String) -> String {
    return val.uppercased()
}

func processValue(_ val: Int) -> Int {
    return val * 2
}

// Function overloading based on types
print(processValue("hello"))        // "HELLO"
print(processValue(5))              // 10
// processValue(true)               // Error: No matching function found
```
</UniversalEditor>

### 2.2 Basic Types Comparison

Swift has more specific types than JavaScript, providing better precision and safety.

<UniversalEditor title="Basic Types Comparison" compare={true}>
```javascript !! js
// JavaScript basic types
let stringValue = "Hello";          // String
let numberValue = 42;               // Number (floating-point)
let booleanValue = true;            // Boolean
let arrayValue = [1, 2, 3];         // Array
let objectValue = { name: "John" }; // Object
let nullValue = null;               // null
let undefinedValue = undefined;     // undefined

// Type checking
console.log(typeof stringValue);    // "string"
console.log(typeof numberValue);    // "number"
console.log(typeof booleanValue);   // "boolean"
console.log(typeof arrayValue);     // "object"
console.log(typeof objectValue);    // "object"
console.log(typeof nullValue);      // "object" (JavaScript quirk)
console.log(typeof undefinedValue); // "undefined"

// Type coercion
console.log("5" + 3);               // "53" (string concatenation)
console.log("5" - 3);               // 2 (numeric subtraction)
console.log(true + 1);              // 2 (boolean to number)
```

```swift !! swift
// Swift basic types
let stringValue: String = "Hello"           // String
let intValue: Int = 42                      // Integer
let doubleValue: Double = 42.0              // Double (floating-point)
let floatValue: Float = 42.0                // Float (32-bit)
let booleanValue: Bool = true               // Boolean
let arrayValue: [Int] = [1, 2, 3]          // Array of Int
let dictionaryValue: [String: String] = ["name": "John"] // Dictionary
let characterValue: Character = "A"         // Single character

// Type checking
print(type(of: stringValue))    // String
print(type(of: intValue))       // Int
print(type(of: doubleValue))    // Double
print(type(of: booleanValue))   // Bool
print(type(of: arrayValue))     // Array<Int>
print(type(of: dictionaryValue)) // Dictionary<String, String>

// No implicit type coercion
// print("5" + 3)               // Error: Cannot convert value of type 'Int' to expected argument type 'String'
print("5" + String(3))          // "53" (explicit conversion)
print(5 + 3)                    // 8 (numeric addition)
```
</UniversalEditor>

## 3. Optional Types

### 3.1 Understanding Optionals

Optionals are Swift's way of handling the absence of a value, similar to `null` and `undefined` in JavaScript, but much safer.

<UniversalEditor title="Optional Types Introduction" compare={true}>
```javascript !! js
// JavaScript null/undefined handling
let name = "John";
let age = 25;
let email = null;           // Explicitly no value
let phone = undefined;      // Undefined value

// Unsafe access - can cause runtime errors
function getLength(str) {
    return str.length;      // Will throw error if str is null/undefined
}

// Safe access with checks
function safeGetLength(str) {
    if (str !== null && str !== undefined) {
        return str.length;
    }
    return 0;
}

// Modern JavaScript with optional chaining
function modernGetLength(str) {
    return str?.length ?? 0;  // Safe access with nullish coalescing
}

console.log(safeGetLength(name));   // 4
console.log(safeGetLength(email));  // 0
console.log(modernGetLength(phone)); // 0
```

```swift !! swift
// Swift optional types
let name: String = "John"
let age: Int = 25
let email: String? = nil           // Optional String (can be nil)
let phone: String? = nil           // Optional String (can be nil)

// Unsafe access - will crash if nil
// let emailLength = email.count   // Error: Value of optional type 'String?' must be unwrapped

// Safe access with optional binding
func getLength(_ str: String?) -> Int {
    if let unwrappedStr = str {
        return unwrappedStr.count
    }
    return 0
}

// Safe access with nil coalescing
func modernGetLength(_ str: String?) -> Int {
    return str?.count ?? 0
}

print(getLength(name))      // 4
print(getLength(email))     // 0
print(modernGetLength(phone)) // 0
```
</UniversalEditor>

### 3.2 Optional Declaration and Unwrapping

Swift provides multiple ways to work with optionals safely.

<UniversalEditor title="Optional Declaration and Unwrapping" compare={true}>
```javascript !! js
// JavaScript optional-like patterns
let optionalValue = null;
let defaultValue = "Default";

// Checking for null/undefined
if (optionalValue !== null && optionalValue !== undefined) {
    console.log(optionalValue);
} else {
    console.log(defaultValue);
}

// Using logical OR (but be careful with falsy values)
let result = optionalValue || defaultValue;
console.log(result); // "Default"

// Using nullish coalescing (modern JavaScript)
let safeResult = optionalValue ?? defaultValue;
console.log(safeResult); // "Default"

// Optional chaining
let user = {
    profile: {
        name: "John",
        email: null
    }
};

console.log(user?.profile?.name);  // "John"
console.log(user?.profile?.email); // null
console.log(user?.profile?.email?.length); // undefined
```

```swift !! swift
// Swift optional declaration and unwrapping
let optionalValue: String? = nil
let defaultValue = "Default"

// Optional binding (if let)
if let unwrappedValue = optionalValue {
    print(unwrappedValue)
} else {
    print(defaultValue)
}

// Guard statement
func processOptional(_ value: String?) {
    guard let unwrappedValue = value else {
        print("Value is nil")
        return
    }
    print("Processing: \(unwrappedValue)")
}

// Nil coalescing operator
let result = optionalValue ?? defaultValue
print(result) // "Default"

// Optional chaining
struct Profile {
    let name: String
    let email: String?
}

struct User {
    let profile: Profile?
}

let user = User(profile: Profile(name: "John", email: nil))

print(user.profile?.name)           // Optional("John")
print(user.profile?.email)          // nil
print(user.profile?.email?.count)   // nil

// Force unwrapping (use with caution)
let forcedValue = user.profile!.name // Will crash if profile is nil
```
</UniversalEditor>

### 3.3 Implicitly Unwrapped Optionals

Swift provides implicitly unwrapped optionals for cases where you're confident a value will exist.

<UniversalEditor title="Implicitly Unwrapped Optionals" compare={true}>
```javascript !! js
// JavaScript - no direct equivalent, but similar patterns
let config = {
    apiUrl: "https://api.example.com",
    timeout: 5000
};

// Assuming config will always have these properties
function makeRequest() {
    // We assume config.apiUrl exists
    console.log(`Making request to ${config.apiUrl}`);
    
    // But we still need to check in practice
    if (!config.apiUrl) {
        throw new Error("API URL is required");
    }
}

// Modern JavaScript with default parameters
function safeMakeRequest(apiUrl = config.apiUrl) {
    if (!apiUrl) {
        throw new Error("API URL is required");
    }
    console.log(`Making request to ${apiUrl}`);
}
```

```swift !! swift
// Swift implicitly unwrapped optionals
let config: [String: String] = [
    "apiUrl": "https://api.example.com",
    "timeout": "5000"
]

// Implicitly unwrapped optional - assumes value exists
let apiUrl: String! = config["apiUrl"]

func makeRequest() {
    // Can use apiUrl directly without unwrapping
    print("Making request to \(apiUrl)")
    
    // But it will crash if apiUrl is nil
    // Use with caution!
}

// Safer approach with regular optionals
func safeMakeRequest() {
    guard let apiUrl = config["apiUrl"] else {
        print("API URL is required")
        return
    }
    print("Making request to \(apiUrl)")
}

// Implicitly unwrapped optionals in IBOutlets (iOS development)
// @IBOutlet weak var label: UILabel!  // Assumes outlet will be connected
```
</UniversalEditor>

## 4. Type Inference

### 4.1 Swift's Type Inference

Swift can often determine the type automatically, reducing the need for explicit type annotations.

<UniversalEditor title="Type Inference" compare={true}>
```javascript !! js
// JavaScript type inference (runtime)
let message = "Hello";       // Inferred as string
let count = 42;              // Inferred as number
let isActive = true;         // Inferred as boolean
let items = [1, 2, 3];       // Inferred as array
let person = { name: "John" }; // Inferred as object

// Type can change at runtime
message = 123;               // Now it's a number
count = "forty-two";         // Now it's a string

// Function parameter types are not inferred
function processData(data) {
    // No compile-time type checking
    return data.length;      // Will work for strings, arrays, but not numbers
}

console.log(processData("hello")); // 5
console.log(processData([1, 2, 3])); // 3
console.log(processData(42)); // undefined (runtime error)
```

```swift !! swift
// Swift type inference (compile-time)
let message = "Hello"        // Inferred as String
let count = 42               // Inferred as Int
let isActive = true          // Inferred as Bool
let items = [1, 2, 3]        // Inferred as [Int]
let person = ["name": "John"] // Inferred as [String: String]

// Type cannot change after inference
// message = 123             // Error: Cannot assign value of type 'Int' to type 'String'
// count = "forty-two"       // Error: Cannot assign value of type 'String' to type 'Int'

// Function parameter types are inferred from usage
func processData(_ data: String) -> Int {
    return data.count
}

func processData(_ data: [Int]) -> Int {
    return data.count
}

print(processData("hello"))  // 5
print(processData([1, 2, 3])) // 3
// processData(42)           // Error: No matching function found
```
</UniversalEditor>

### 4.2 Type Annotations

Explicit type annotations can make code clearer and help catch errors early.

<UniversalEditor title="Type Annotations" compare={true}>
```javascript !! js
// JavaScript - no static type annotations (without TypeScript)
let name = "John";
let age = 25;
let scores = [85, 90, 78];

// With TypeScript (for comparison)
// let name: string = "John";
// let age: number = 25;
// let scores: number[] = [85, 90, 78];

// Function parameters without type checking
function calculateAverage(numbers) {
    const sum = numbers.reduce((acc, num) => acc + num, 0);
    return sum / numbers.length;
}

// Runtime type checking
function safeCalculateAverage(numbers) {
    if (!Array.isArray(numbers)) {
        throw new Error("Input must be an array");
    }
    
    if (numbers.length === 0) {
        return 0;
    }
    
    const sum = numbers.reduce((acc, num) => {
        if (typeof num !== 'number') {
            throw new Error("All elements must be numbers");
        }
        return acc + num;
    }, 0);
    
    return sum / numbers.length;
}

console.log(calculateAverage([1, 2, 3, 4, 5])); // 3
console.log(safeCalculateAverage([1, 2, 3, 4, 5])); // 3
```

```swift !! swift
// Swift - explicit type annotations
let name: String = "John"
let age: Int = 25
let scores: [Int] = [85, 90, 78]

// Function with explicit types
func calculateAverage(_ numbers: [Int]) -> Double {
    guard !numbers.isEmpty else { return 0.0 }
    
    let sum = numbers.reduce(0, +)
    return Double(sum) / Double(numbers.count)
}

// Function with optional return type
func findFirstEven(_ numbers: [Int]) -> Int? {
    return numbers.first { $0 % 2 == 0 }
}

// Function with multiple return types
func analyzeNumbers(_ numbers: [Int]) -> (min: Int, max: Int, average: Double) {
    let min = numbers.min() ?? 0
    let max = numbers.max() ?? 0
    let average = calculateAverage(numbers)
    
    return (min: min, max: max, average: average)
}

print(calculateAverage([1, 2, 3, 4, 5])) // 3.0
print(findFirstEven([1, 3, 5, 2, 4])) // Optional(2)
let analysis = analyzeNumbers([1, 2, 3, 4, 5])
print("Min: \(analysis.min), Max: \(analysis.max), Avg: \(analysis.average)")
```
</UniversalEditor>

## 5. Type Conversion

### 5.1 Type Conversion Methods

Swift requires explicit type conversion, preventing accidental data loss.

<UniversalEditor title="Type Conversion" compare={true}>
```javascript !! js
// JavaScript type conversion (implicit and explicit)
let stringNum = "42";
let actualNum = 42;
let floatNum = 42.5;

// Implicit conversion (can be problematic)
console.log(stringNum + actualNum);     // "4242" (string concatenation)
console.log(stringNum - actualNum);     // 0 (numeric subtraction)
console.log(actualNum + floatNum);      // 84.5 (numeric addition)

// Explicit conversion
console.log(Number(stringNum));         // 42
console.log(String(actualNum));         // "42"
console.log(Boolean(actualNum));        // true
console.log(parseInt("42.5"));          // 42
console.log(parseFloat("42.5"));        // 42.5

// Array conversion
console.log(Array.from("hello"));       // ["h", "e", "l", "l", "o"]
console.log([...new Set([1, 2, 2, 3])]); // [1, 2, 3]

// Object conversion
console.log(Object.keys({a: 1, b: 2})); // ["a", "b"]
console.log(Object.values({a: 1, b: 2})); // [1, 2]
```

```swift !! swift
// Swift type conversion (explicit only)
let stringNum = "42"
let actualNum = 42
let floatNum = 42.5

// No implicit conversion
// print(stringNum + actualNum)         // Error: Cannot convert value of type 'Int' to expected argument type 'String'

// Explicit conversion
print(Int(stringNum) ?? 0)              // 42 (with nil coalescing)
print(String(actualNum))                // "42"
print(Bool(actualNum))                  // true
print(Double(actualNum))                // 42.0

// String to number conversion
if let number = Int("42.5") {
    print(number)                       // Won't print (conversion fails)
}
if let number = Double("42.5") {
    print(number)                       // 42.5
}

// Array conversion
let charArray = Array("hello")          // ["h", "e", "l", "l", "o"]
let uniqueArray = Array(Set([1, 2, 2, 3])) // [1, 2, 3]

// Dictionary conversion
let dict = ["a": 1, "b": 2]
print(Array(dict.keys))                 // ["a", "b"]
print(Array(dict.values))               // [1, 2]
```
</UniversalEditor>

### 5.2 Type Checking

Swift provides compile-time type checking and runtime type checking capabilities.

<UniversalEditor title="Type Checking" compare={true}>
```javascript !! js
// JavaScript type checking
function processValue(value) {
    // Runtime type checking
    if (typeof value === "string") {
        return value.toUpperCase();
    } else if (typeof value === "number") {
        return value * 2;
    } else if (Array.isArray(value)) {
        return value.length;
    } else if (typeof value === "object" && value !== null) {
        return Object.keys(value).length;
    } else {
        return "Unknown type";
    }
}

// Instance checking
function checkInstance(value) {
    if (value instanceof Array) {
        return "Array";
    } else if (value instanceof Date) {
        return "Date";
    } else if (value instanceof RegExp) {
        return "RegExp";
    } else {
        return "Other";
    }
}

console.log(processValue("hello"));     // "HELLO"
console.log(processValue(5));           // 10
console.log(processValue([1, 2, 3]));   // 3
console.log(processValue({a: 1, b: 2})); // 2

console.log(checkInstance([1, 2, 3]));  // "Array"
console.log(checkInstance(new Date())); // "Date"
```

```swift !! swift
// Swift type checking
func processValue(_ value: Any) -> String {
    // Runtime type checking with type casting
    if let stringValue = value as? String {
        return stringValue.uppercased()
    } else if let intValue = value as? Int {
        return String(intValue * 2)
    } else if let arrayValue = value as? [Any] {
        return String(arrayValue.count)
    } else if let dictValue = value as? [String: Any] {
        return String(dictValue.count)
    } else {
        return "Unknown type"
    }
}

// Type checking with switch statement
func checkType(_ value: Any) -> String {
    switch value {
    case is String:
        return "String"
    case is Int:
        return "Int"
    case is [Any]:
        return "Array"
    case is [String: Any]:
        return "Dictionary"
    default:
        return "Other"
    }
}

// Type checking with pattern matching
func processWithPattern(_ value: Any) -> String {
    switch value {
    case let string as String:
        return "String: \(string)"
    case let number as Int:
        return "Int: \(number)"
    case let array as [Any]:
        return "Array with \(array.count) elements"
    default:
        return "Unknown type"
    }
}

print(processValue("hello"))            // "HELLO"
print(processValue(5))                  // "10"
print(processValue([1, 2, 3]))          // "3"
print(processValue(["a": 1, "b": 2]))   // "2"

print(checkType("hello"))               // "String"
print(checkType(42))                    // "Int"
print(processWithPattern([1, 2, 3]))    // "Array with 3 elements"
```
</UniversalEditor>

## 6. Type Aliases and Custom Types

### 6.1 Type Aliases

Swift allows you to create type aliases for better code readability.

<UniversalEditor title="Type Aliases" compare={true}>
```javascript !! js
// JavaScript - no direct type aliases, but similar patterns
// Using constants for type names
const UserId = String;
const UserAge = Number;
const UserEmail = String;

// Using JSDoc for type documentation
/**
 * @typedef {Object} User
 * @property {string} id
 * @property {string} name
 * @property {number} age
 * @property {string} email
 */

/**
 * @param {User} user
 * @returns {string}
 */
function formatUser(user) {
    return `${user.name} (${user.age}) - ${user.email}`;
}

// Using TypeScript (for comparison)
// type UserId = string;
// type UserAge = number;
// type UserEmail = string;

// interface User {
//     id: UserId;
//     name: string;
//     age: UserAge;
//     email: UserEmail;
// }

const user = {
    id: "123",
    name: "John",
    age: 25,
    email: "john@example.com"
};

console.log(formatUser(user));
```

```swift !! swift
// Swift type aliases
typealias UserId = String
typealias UserAge = Int
typealias UserEmail = String

// Using type aliases in structs
struct User {
    let id: UserId
    let name: String
    let age: UserAge
    let email: UserEmail
}

// Function using type aliases
func formatUser(_ user: User) -> String {
    return "\(user.name) (\(user.age)) - \(user.email)"
}

// Type aliases for complex types
typealias UserList = [User]
typealias UserDictionary = [UserId: User]
typealias UserFilter = (User) -> Bool

// Using complex type aliases
func filterUsers(_ users: UserList, by filter: UserFilter) -> UserList {
    return users.filter(filter)
}

func findUser(by id: UserId, in users: UserDictionary) -> User? {
    return users[id]
}

let user = User(
    id: "123",
    name: "John",
    age: 25,
    email: "john@example.com"
)

print(formatUser(user))

// Type aliases for closures
typealias CompletionHandler = (Result<User, Error>) -> Void
typealias NetworkCallback = (Data?, Error?) -> Void
```
</UniversalEditor>

## 7. Practice Exercises

### Exercise 1: Optional Handling

<UniversalEditor title="Exercise 1: Optional Handling" compare={true}>
```javascript !! js
// JavaScript optional handling exercise
function processUserData(userData) {
    // Handle potentially missing user data
    const name = userData?.name || "Unknown";
    const age = userData?.age || 0;
    const email = userData?.email || "No email";
    
    return {
        displayName: name,
        ageGroup: age < 18 ? "Minor" : age < 65 ? "Adult" : "Senior",
        hasEmail: email !== "No email"
    };
}

function safeStringLength(str) {
    // Handle null/undefined strings
    if (str === null || str === undefined) {
        return 0;
    }
    return str.length;
}

function findFirstPositive(numbers) {
    // Find first positive number or return null
    for (let num of numbers) {
        if (num > 0) {
            return num;
        }
    }
    return null;
}

// Test the functions
console.log(processUserData({ name: "John", age: 25, email: "john@example.com" }));
console.log(processUserData({ name: "Jane", age: 16 }));
console.log(processUserData(null));

console.log(safeStringLength("hello")); // 5
console.log(safeStringLength(null)); // 0

console.log(findFirstPositive([-1, -2, 3, -4, 5])); // 3
console.log(findFirstPositive([-1, -2, -3])); // null
```

```swift !! swift
// Swift optional handling exercise
struct UserData {
    let name: String?
    let age: Int?
    let email: String?
}

func processUserData(_ userData: UserData?) -> (displayName: String, ageGroup: String, hasEmail: Bool) {
    // Handle potentially missing user data
    let name = userData?.name ?? "Unknown"
    let age = userData?.age ?? 0
    let email = userData?.email
    
    let ageGroup: String
    if age < 18 {
        ageGroup = "Minor"
    } else if age < 65 {
        ageGroup = "Adult"
    } else {
        ageGroup = "Senior"
    }
    
    return (
        displayName: name,
        ageGroup: ageGroup,
        hasEmail: email != nil
    )
}

func safeStringLength(_ str: String?) -> Int {
    // Handle optional strings
    return str?.count ?? 0
}

func findFirstPositive(_ numbers: [Int]) -> Int? {
    // Find first positive number or return nil
    return numbers.first { $0 > 0 }
}

// Test the functions
let user1 = UserData(name: "John", age: 25, email: "john@example.com")
let user2 = UserData(name: "Jane", age: 16, email: nil)
let user3: UserData? = nil

print(processUserData(user1))
print(processUserData(user2))
print(processUserData(user3))

print(safeStringLength("hello")) // 5
print(safeStringLength(nil)) // 0

print(findFirstPositive([-1, -2, 3, -4, 5])) // Optional(3)
print(findFirstPositive([-1, -2, -3])) // nil
```
</UniversalEditor>

### Exercise 2: Type Conversion and Checking

<UniversalEditor title="Exercise 2: Type Conversion and Checking" compare={true}>
```javascript !! js
// JavaScript type conversion exercise
function convertToNumber(value) {
    const num = Number(value);
    return isNaN(num) ? null : num;
}

function convertToString(value) {
    if (value === null || value === undefined) {
        return "null";
    }
    return String(value);
}

function validateUserInput(input) {
    const errors = [];
    
    if (typeof input.name !== "string" || input.name.trim() === "") {
        errors.push("Name must be a non-empty string");
    }
    
    if (typeof input.age !== "number" || input.age < 0 || input.age > 150) {
        errors.push("Age must be a number between 0 and 150");
    }
    
    if (typeof input.email !== "string" || !input.email.includes("@")) {
        errors.push("Email must be a valid email address");
    }
    
    return errors;
}

// Test the functions
console.log(convertToNumber("42")); // 42
console.log(convertToNumber("abc")); // null
console.log(convertToNumber("42.5")); // 42.5

console.log(convertToString(42)); // "42"
console.log(convertToString(null)); // "null"
console.log(convertToString([1, 2, 3])); // "1,2,3"

const validInput = { name: "John", age: 25, email: "john@example.com" };
const invalidInput = { name: "", age: "25", email: "invalid-email" };

console.log(validateUserInput(validInput)); // []
console.log(validateUserInput(invalidInput)); // ["Name must be a non-empty string", "Age must be a number between 0 and 150", "Email must be a valid email address"]
```

```swift !! swift
// Swift type conversion exercise
func convertToNumber(_ value: String) -> Int? {
    return Int(value)
}

func convertToString(_ value: Any) -> String {
    if let optionalValue = value as? Optional<Any> {
        return "nil"
    }
    return String(describing: value)
}

struct UserInput {
    let name: String
    let age: Int
    let email: String
}

func validateUserInput(_ input: UserInput) -> [String] {
    var errors: [String] = []
    
    if input.name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
        errors.append("Name must be a non-empty string")
    }
    
    if input.age < 0 || input.age > 150 {
        errors.append("Age must be a number between 0 and 150")
    }
    
    if !input.email.contains("@") {
        errors.append("Email must be a valid email address")
    }
    
    return errors
}

// Test the functions
print(convertToNumber("42")) // Optional(42)
print(convertToNumber("abc")) // nil
print(convertToNumber("42.5")) // nil (Int conversion fails for decimals)

print(convertToString(42)) // "42"
print(convertToString(nil as String?)) // "nil"
print(convertToString([1, 2, 3])) // "[1, 2, 3]"

let validInput = UserInput(name: "John", age: 25, email: "john@example.com")
let invalidInput = UserInput(name: "", age: 200, email: "invalid-email")

print(validateUserInput(validInput)) // []
print(validateUserInput(invalidInput)) // ["Name must be a non-empty string", "Age must be a number between 0 and 150", "Email must be a valid email address"]
```
</UniversalEditor>

## 8. Key Takeaways

### 8.1 Type System Benefits

| Feature | JavaScript | Swift | Benefit |
|---|---|---|---|
| **Type Safety** | Runtime checking | Compile-time checking | Catches errors before execution |
| **Performance** | Dynamic dispatch | Static dispatch | Better performance |
| **Tooling** | Limited IDE support | Rich IDE support | Better autocomplete and refactoring |
| **Documentation** | Comments/JSDoc | Type annotations | Self-documenting code |
| **Refactoring** | Error-prone | Safe | Confident code changes |

### 8.2 Optional Best Practices

1. **Use Optionals for Missing Values**: Don't use sentinel values like `-1` or `""`
2. **Prefer Optional Binding**: Use `if let` and `guard let` over force unwrapping
3. **Use Nil Coalescing**: `??` operator for default values
4. **Avoid Force Unwrapping**: Only use `!` when you're absolutely certain
5. **Use Optional Chaining**: `?.` for safe property access
6. **Return Optionals**: When a function might not have a result

### 8.3 Type Safety Best Practices

1. **Use Type Annotations**: Explicit types improve code clarity
2. **Leverage Type Inference**: Let Swift infer types when obvious
3. **Use Type Aliases**: For complex types and better readability
4. **Prefer Strong Types**: Use specific types over `Any`
5. **Handle Optionals Safely**: Always consider the nil case
6. **Use Type Checking**: `is` and `as?` for runtime type checking

### 8.4 Common Pitfalls

1. **Force Unwrapping**: Using `!` without checking for nil
2. **Ignoring Optionals**: Not handling the possibility of nil
3. **Type Any**: Using `Any` when specific types would be better
4. **Implicit Conversion**: Expecting automatic type conversion
5. **Runtime Errors**: Not using compile-time type checking effectively

## 9. Next Steps

In the next module, we'll explore functions and closures in detail, including:
- Function types and higher-order functions
- Closure syntax and capture semantics
- Function overloading and default parameters
- Functional programming patterns in Swift

This foundation in Swift's type system will prepare you for more advanced concepts and help you write safer, more reliable code. 